Mike (@mikerb95)CodeByMike
Volver
/architectureDiseño de sistemas

Cómo está construido esto.

Este sitio es un sistema en producción: un panel privado, un portal de clientes, cobros reales, monitoreo y seguridad propios, y agentes de IA. Aquí está su anatomía, de la petición del visitante a la base de datos, y el porqué de cada pieza. Casi todas se pueden ver funcionando.

Elige quién llega y mira hasta dónde llega de verdad. El recorrido sale de las mismas funciones que usa el middleware, así que no puede contar otra cosa.

Primera visitaGET /status
  1. HTML SSR + islasEl navegador pide la página.
  2. WAF de VercelEl WAF de Vercel no ve nada raro y la deja pasar.
  3. CDN con cachéLa CDN no tiene copia fresca (MISS): hay que generar la página.
  4. Funciones NodeDespierta una función Node de Vercel.
  5. Rutas en un idiomaEl middleware quita el idioma y la barra final: todo guardia ve la ruta canónica.
  6. Micro-SIEMEl sensor del micro-SIEM la clasifica: tráfico normal.
  7. Sitio públicoAstro pinta la página pública en el servidor.
  8. Lecturas con presupuestoLee el resumen diario de los monitores, no las filas crudas: Turso cobra filas escaneadas.

200 · MISSLa respuesta queda 5 minutos en la CDN: esta petición pagó el SSR por las siguientes.

En los 5 minutos siguientesGET /status
  1. HTML SSR + islasEl navegador pide la página.
  2. WAF de VercelEl WAF de Vercel no ve nada raro y la deja pasar.
  3. CDN con cachéLa CDN tiene una copia de menos de 5 minutos (HIT) y responde ella.

200 · HITNi función ni base: el SSR se pagó una vez y se sirve muchas.

Con una herramienta conocidaGET /
  1. WAF de VercelEl user-agent es de una herramienta de ataque: el WAF la corta en el borde, sin despertar ninguna función.

403Muere antes de costar una sola invocación.

Disfrazado de navegadorGET /.env
  1. WAF de VercelLa ruta huele a sondeo, pero esa regla del WAF solo anota: bloquear aquí dejaría ciego al sensor propio.
  2. CDN con cachéNada que servir desde la caché.
  3. Funciones NodeDespierta una función Node de Vercel.
  4. Rutas en un idiomaEl middleware quita el idioma y la barra final: todo guardia ve la ruta canónica.
  5. Micro-SIEMEl sensor la reconoce como búsqueda de secretos y la anota en el micro-SIEM.
  6. Sitio públicoNo hay nada en esa ruta: 404.

404Recibe un 404 y queda anotado. Si insiste, el rate limit y el bloqueo escalado hacen el resto.

GET /en/admin
  1. HTML SSR + islasEl navegador pide la página.
  2. WAF de VercelEl WAF de Vercel no ve nada raro y la deja pasar.
  3. CDN con cachéNada que servir desde la caché.
  4. Funciones NodeDespierta una función Node de Vercel.
  5. Rutas en un idiomaUn /en/ delante de una ruta privada no tiene traducción: 404 antes que cualquier otra cosa.

404Ni el sensor ni la sesión llegan a correr: el panel no tiene copia en inglés.

GET /admin
  1. HTML SSR + islasEl navegador pide la página.
  2. WAF de VercelEl WAF de Vercel no ve nada raro y la deja pasar.
  3. CDN con cachéRuta privada: la CDN no guarda copia y la deja pasar.
  4. Funciones NodeDespierta una función Node de Vercel.
  5. Rutas en un idiomaEl middleware quita el idioma y la barra final: todo guardia ve la ruta canónica.
  6. Micro-SIEMEl sensor del micro-SIEM la clasifica: tráfico normal.
  7. Auth del panelSesión de GitHub en la allowlist y, según Turso, sin revocar.
  8. Panel /adminSe pinta el panel, sin caché en ningún lado.
  9. Dos basesLas consultas van a la base de producción.

200200 privado, sin caché y con cabeceras endurecidas.

GET /portal/facturas
  1. HTML SSR + islasEl navegador pide la página.
  2. WAF de VercelEl WAF de Vercel no ve nada raro y la deja pasar.
  3. CDN con cachéRuta privada: la CDN no guarda copia y la deja pasar.
  4. Funciones NodeDespierta una función Node de Vercel.
  5. Rutas en un idiomaEl middleware quita el idioma y la barra final: todo guardia ve la ruta canónica.
  6. Micro-SIEMEl sensor del micro-SIEM la clasifica: tráfico normal.
  7. Portal de clientesCookie propia del portal, contrastada con su tabla de sesiones.
  8. Dos basesCada consulta filtra por el cliente de la sesión, nunca por uno que venga en la URL.

200El cliente solo ve lo suyo.

Leyendo el panelGET /admin/finances
  1. HTML SSR + islasEl navegador pide la página.
  2. WAF de VercelEl WAF de Vercel no ve nada raro y la deja pasar.
  3. CDN con cachéRuta privada: la CDN no guarda copia y la deja pasar.
  4. Funciones NodeDespierta una función Node de Vercel.
  5. Rutas en un idiomaEl middleware quita el idioma y la barra final: todo guardia ve la ruta canónica.
  6. Micro-SIEMEl sensor del micro-SIEM la clasifica: tráfico normal.
  7. Auth del panelSin sesión, pero con un pase de demo firmado (HMAC): entra en solo lectura.
  8. Demo públicaSe pinta el panel de verdad, en modo demo.
  9. Dos basesLas consultas salen de la base de demo, otra base distinta, con datos ficticios.

200El panel real, con datos inventados.

Pidiendo la bóvedaGET /api/admin/services/7/secrets
  1. HTML SSR + islasEl navegador pide la página.
  2. WAF de VercelEl WAF de Vercel no ve nada raro y la deja pasar.
  3. CDN con cachéRuta privada: la CDN no guarda copia y la deja pasar.
  4. Funciones NodeDespierta una función Node de Vercel.
  5. Rutas en un idiomaEl middleware quita el idioma y la barra final: todo guardia ve la ruta canónica.
  6. Micro-SIEMEl sensor del micro-SIEM la clasifica: tráfico normal.
  7. Auth del panelLa bóveda revela secretos y es un GET: está vetada por patrón en la demo. 403.

403"Solo lectura" no bastaba: un GET también puede revelar.

Pieza por pieza

Cliente
Navegador del visitante
HTML SSR + islas
Astro entrega HTML ya renderizado; JS solo donde la página lo necesita.
RUM web-vitals
Mide LCP, INP y CLS reales en el navegador y los manda por beacon.
Borde
Vercel: CDN, WAF y funciones
CDN con caché
Las páginas públicas se cachean 5 minutos en el borde: el SSR se paga una vez y se sirve muchas.
WAF de Vercel
Tres reglas que frenan escáneres conocidos antes de que lleguen a una función.
Funciones Node
Rutas dinámicas y API, dentro de la cuota del plan gratuito.
Puerta
Middleware: la seguridad va antes que la página
Micro-SIEM
Clasifica cada petición, aplica rate limit durable y bloqueo escalado. Falla abierto: si se cae, el sitio sigue.
Auth del panel
Allowlist revalidada en cada petición y sesiones revocables. La revocación falla cerrada.
Rutas en un idioma
La ruta se normaliza antes de cualquier guarda: /en/admin no es una copia del panel sin vigilancia.
Chaos flags
Fallos inyectados a propósito para ensayar, acotados para no poder romper producción.
Aplicación
Astro 7 SSR: tres públicos, tres puertas
Sitio público
Vitrinas que muestran agregados, nunca datos que sirvan de manual de ataque.
Panel /admin
CRM, finanzas, bóveda cifrada, monitores, laboratorio y el analista de seguridad.
Portal de clientes
Su propio login y sus propias cookies. El cliente sale de la sesión, nunca de la URL.
Demo pública
El panel de verdad contra una base distinta, solo lectura.
Cobros
Links de pago con idempotencia y webhooks firmados, cobrados por WhatsApp sin su API.
Datos
Turso (libSQL) + Drizzle
Dos bases
Producción y demo separadas; el modo demo se elige por petición.
Migraciones aditivas
Esquema tipado en un solo archivo; nunca se borra una columna sin pedirlo.
Bóveda AES-256-GCM
Credenciales cifradas en reposo y reveladas solo bajo sesión de admin.
Lecturas con presupuesto
Turso cobra filas escaneadas: lo que se consulta seguido va agregado por día.
En segundo plano
Tareas programadas y agentes
Crons vigilados
Cada corrida deja rastro y un detector avisa si un cron se queda callado.
Monitor propio
Sondeos cada 5 minutos, incidentes y SLO sin servicios de pago.
Analista de seguridad
Un agente con Claude que lee los ataques y propone bloqueos; decide una persona.
Registro de cambios
Lo que cambió cada semana, sacado de los commits reales.

Servicios externos

Vercel
Hosting, CDN, WAF, previews por PR y rollback automático desde CI.
Turso
Base libSQL de producción y la de la demo, en el plan gratuito.
cron-job.org
Dispara el sondeo cada 5 minutos: el plan gratuito de Vercel solo permite crons diarios.
GitHub
Repositorio público, CI con tests y cobertura, y el login del panel (OAuth).
API de Claude
El analista de seguridad, el asesor de la burbuja y el resumen semanal. Solo con API key; el analista y el asesor, con tope de gasto diario.
ntfy
Notificaciones al celular: caídas, silencios de cron, análisis de la mañana.
Resend
Correo transaccional, como canal secundario.
Wompi
Pasarela de pagos en Colombia para los links de cobro.
Vercel Blob
Respaldos de la base y documentos del portal, en un almacén privado.

Decisiones y trade-offs

→
SSR, pero cacheado en el borde

El panel y el portal necesitan datos en vivo y una sesión por petición. Las páginas públicas se cachean 5 minutos en la CDN, así que el SSR se paga solo donde aporta.

Verlo pasar: Un visitante
→
Fail-open en todo, salvo en la autorización

Si el sensor de seguridad, el rate limiter o una notificación fallan, la petición sigue: una defensa que puede tumbar el sitio es una superficie de ataque nueva. La excepción es la revocación de sesiones del panel, que falla cerrada: es permiso, no observabilidad.

Verlo pasar: Yo, en el panel · Turso caído
→
JWT sin estado, pero revocable

El login no toca la base. El punto ciego de un JWT (no poder revocarlo) se cerró con un identificador de sesión firmado dentro del token y una tabla de dispositivos.

Verlo pasar: Yo, en el panel
→
Turso en vez de Postgres

SQLite gestionado, con un modelo mental simple para un dominio sin concurrencia masiva. El precio es pensar cada consulta en filas escaneadas: lo que se lee mucho va agregado.

Verlo pasar: Un visitante
→
Crons externos con bitácora

Un cron que deja de dispararse no da error: da silencio. Por eso cada corrida queda registrada y un vigilante avisa al celular cuando alguno lleva demasiado tiempo callado.

→
La IA propone; decide una persona

Los agentes leen datos con herramientas de solo lectura y nunca ven una IP. Cualquier acción, como un bloqueo, espera la aprobación de una persona, y los que pueden correr muchas veces al día (el analista y el asesor) tienen un tope de gasto diario.

→
Docker para desarrollar, no para producción

Docker fija la versión de Node y levanta las bases locales para las pruebas. En producción, contenerizar perdería el borde, las previews por PR y el rollback automático.

Límites conocidos

  • La CSP todavía permite estilos y scripts inline; quitarlo exige hashes o nonces en todas las páginas.
  • Un solo proveedor de base de datos: si Turso cae, el panel cae con él (las páginas públicas aguantan con la caché y sus fallbacks).
  • El sondeo de 5 minutos depende de cron-job.org; el detector de silencio lo vigila, pero no lo reemplaza.
  • Sin entorno de staging propio: las previews de Vercel hacen ese papel contra bases desechables.

Todo el código vive en un repositorio público. Las decisiones de aquí no son teoría: cada una tiene su commit, sus tests y, cuando aplica, su hallazgo de seguridad documentado en /security.