Auditoría legal de un blog real: lo que encontramos en codigoagente y cómo lo corregimos
En la primera parte repasamos qué leyes de EE. UU., Europa y Ecuador afectan a una app o web hecha con IA y armamos un prompt de auditoría. Aquí lo aplicamos a un caso real: este mismo blog, un sitio en Next.js 16 y Sanity desplegado en Vercel, montado en buena parte con Claude Code. Verás el inventario, las pruebas con curl, las capturas del antes y el después y el código que cambiamos.
El inventario: qué hace realmente el sitio
Antes de corregir nada, listamos lo que el sitio hace con los datos de quien lo visita:
| Pregunta | Respuesta en codigoagente |
|---|---|
| ¿Registro, formularios o comentarios? | No |
| ¿Newsletter o emails de marketing? | No |
| ¿Suscripciones o pagos? | No |
| ¿Analítica, publicidad o grabación de sesiones? | No |
| ¿Recursos de terceros? | Imágenes desde `cdn.sanity.io` (CDN de Sanity) |
| ¿Enlaces de afiliado? | Sí, a Contabo en la guía de Paymenter |
| ¿Contacto? | Un correo, `contacto@codigoagente.com` |
| ¿Público? | Ecuador, resto de Latinoamérica, España y lectores en inglés |
| ¿Funciones de IA para el lector? | No (el contenido se escribe con ayuda de IA, pero el lector no habla con ninguna) |
Con eso, varias normas quedan fuera: COPPA (no es un servicio para niños ni pide datos), CIPA (no hay grabación ni chat), CAN-SPAM y el art. 50 de la Ley de Comercio Electrónico de Ecuador (no hay emails), ROSCA (no hay suscripciones) y DMCA (los lectores no publican nada). Siguen en juego la LOPDP de Ecuador, el RGPD por los lectores de España y las guías de la FTC sobre afiliados.
Prueba 1: fuentes y recursos de terceros
El sitio usa las tipografías Geist con next/font/google. Ese módulo descarga las fuentes durante el build y las sirve desde el propio dominio, así que el navegador del lector nunca contacta con Google. Lo comprobamos sobre la versión publicada:
curl -s https://codigoagente.vercel.app/ | grep -c "fonts.googleapis.com\|fonts.gstatic.com"
# 0
curl -s https://codigoagente.vercel.app/ | grep -oE '[^"]*\.woff2' | head -2
# /_next/static/immutable/media/797e433ab948586e-s.p.1v5bejj26fx9h.woff2
# /_next/static/immutable/media/caa3a2e1cccd8315-s.p.0zr6hhvz-h9nw.woff2Resultado: correcto. Cero llamadas a Google; las fuentes salen de /_next/static/.
Listando todos los dominios externos de un artículo con imágenes apareció otro tercero:
curl -s https://codigoagente.vercel.app/blog/instalar-paymenter-vps-coolify-docker \
| grep -oE '(src|href)="https?://[^"/]+' | sort | uniq -c
# 3 href="https://contabo.com
# 1 href="https://github.com
# 2 href="https://safebuildlabs.com
# 9 src="https://cdn.sanity.ioLos href son enlaces que el lector decide pulsar. Los src de cdn.sanity.io no: el navegador los carga solo y Sanity recibe la IP del lector, igual que Google con las fuentes. Tenemos dos opciones: servir las imágenes a través del propio dominio (por ejemplo con next/image, a cambio de consumir cuota de optimización de Vercel) o declararlo con transparencia. Elegimos lo segundo por ahora: Sanity ya era encargado del tratamiento y ahora la política dice exactamente qué recibe.
Prueba 2: cookies
curl -sI https://codigoagente.vercel.app/ | grep -ci "set-cookie"
# 0El sitio público no instala cookies ni usa localStorage (lo confirmamos buscando document.cookie, localStorage y cookies() en src/). Pero la política de cookies decía otra cosa: describía un banner de consentimiento que no existe, cookies de AdSense que nunca se activaron y dejaba huecos como [PROVEEDOR PENDIENTE].

Corrección: reescribimos src/app/(es)/cookies/page.tsx con un inventario real (páginas y fuentes desde Vercel, imágenes desde Sanity, sin analítica ni publicidad) y explicamos por qué no hay banner. Si mañana se añade analítica, la página ya dice que se pedirá consentimiento antes.
Prueba 3: la política de privacidad
El mismo problema, más grave porque la LOPDP (art. 12) obliga a informar con lealtad y transparencia. La versión anterior hablaba de "analítica agregada" y "publicidad mediante Google AdSense" con base en el consentimiento, de un banner y de conservar logs "un máximo de [N] días".

Corrección en src/app/(es)/privacidad/page.tsx:
- Datos tratados: solo los técnicos de la conexión y los correos que recibimos.
- Proveedores con nombre: Vercel (alojamiento), Sanity (contenido y CDN de imágenes) y el reenvío de correo del dominio.
- Plazo concreto para atender derechos: 15 días, el que fija la LOPDP.
- Reclamo ante la Superintendencia de Protección de Datos Personales.
- Compromiso de actualizar la política antes de añadir analítica, publicidad o formularios.
También quitamos el "[N] días laborables" de la página de contacto y la mención a "espacios publicitarios" de los términos, porque el sitio no muestra anuncios.
Prueba 4: enlaces de afiliado
La guía de Paymenter enlaza a Contabo con enlace de afiliado. El sitio ya marcaba esos enlaces con rel="sponsored" para Google y mostraba una caja "Herramientas mencionadas" con la divulgación, pero al final del artículo. El lector que pulsaba el enlace del tercer párrafo no había visto ningún aviso, y la FTC pide la divulgación cerca del enlace.
Corrección 1: un aviso al principio de cada artículo que tenga afiliados.

Corrección 2: una marca «afiliado» junto a cada enlace pagado dentro del texto.

El cambio está en src/components/organisms/post-body.tsx: el componente que pinta los enlaces de Portable Text ya sabía si un enlace era sponsored, así que basta con añadir la etiqueta al lado:
link: ({ value, children }) => {
const href: string = value?.href ?? "#";
const external = href.startsWith("http");
const sponsored = Boolean(value?.sponsored);
const rel = sponsored ? "sponsored noopener noreferrer" : "noopener noreferrer";
return (
<>
<a href={href} {...(external ? { target: "_blank", rel } : {})}>
{children}
</a>
{sponsored ? (
<span className="ml-1 rounded border px-1 align-middle font-mono text-[0.7em] text-muted-foreground no-underline">
{affiliateMark}
</span>
) : null}
</>
);
},El texto de la marca y del aviso sale del diccionario src/lib/i18n.ts, así que en inglés aparece «affiliate». El aviso superior se añade en src/components/templates/post-template.tsx cuando el artículo tiene affiliateLinks.
Lo que queda pendiente (y por qué no lo arreglamos solos)
Una auditoría honesta también deja por escrito lo que no se cierra en el código:
- Datos del responsable. La LOPDP (art. 12, numeral 8) pide informar domicilio, teléfono y correo del responsable del tratamiento. codigoagente es una marca anónima y hoy solo publica el correo y la ciudad. Cumplirlo sin exponer a una persona pasa, por ejemplo, por operar el blog desde una empresa o con una dirección comercial. Es una decisión de negocio, no de código.
- Políticas en inglés. Las páginas legales solo existen en español y el pie del sitio en inglés lo avisa. Para lectores de EE. UU. y la UE conviene tener una versión en inglés.
- Imágenes desde el CDN de Sanity. Está declarado, pero si el sitio crece con lectores en la UE, servirlas desde el propio dominio elimina la cuestión de raíz.
- Contenido escrito con IA. El AI Act obliga a avisar del texto generado con IA que se publica para informar sobre asuntos de interés público, salvo que pase revisión humana con responsabilidad editorial. Este blog es técnico y se revisa antes de publicar, pero una nota sobre cómo se escriben los artículos suma transparencia.
Resumen de cambios
| Riesgo | Norma | ¿Aplica? | Cambio | Archivo |
|---|---|---|---|---|
| Fuentes de Google | RGPD | Sí | Ya cumplía con `next/font` | `src/app/layout.tsx` |
| Imágenes de terceros | RGPD / LOPDP | Sí | Declarado en privacidad y cookies | `privacidad/page.tsx`, `cookies/page.tsx` |
| Política que no coincide con la realidad | LOPDP art. 12 | Sí | Reescrita | `privacidad/page.tsx`, `cookies/page.tsx` |
| Huecos en contacto y términos | LOPDP | Sí | Plazo de 15 días, sin publicidad | `contacto/page.tsx`, `terminos/page.tsx` |
| Afiliados sin aviso cerca | FTC | Sí | Aviso arriba y marca junto al enlace | `post-body.tsx`, `post-template.tsx`, `i18n.ts` |
| Menores | COPPA / LOPDP art. 21 | No | — | — |
| Grabación de sesiones | CIPA | No | — | — |
| Emails de marketing | CAN-SPAM / LCE art. 50 | No | — | — |
| Suscripciones | ROSCA / ARL California | No | — | — |
| Contenido de usuarios | DMCA | No | — | — |
Antes de subir los cambios pasamos pnpm lint y pnpm build sin errores, y comprobamos en un servidor local de producción que el aviso y las marcas aparecen solo en los artículos con afiliados.
Preguntas frecuentes
¿Cuánto se tarda en hacer una auditoría así?
Para un blog como este, el inventario y las pruebas con curl llevan menos de una hora. Las correcciones de código, otra hora. Lo que más tiempo lleva es decidir los pendientes que no son técnicos.
¿Puedo usar el mismo prompt en mi proyecto?
Sí, es el de la primera parte. Lo importante es el paso 1: que Claude haga el inventario antes de tocar nada y te pregunte lo que no puede deducir.
¿Por qué no añadir un banner de cookies "por si acaso"?
Porque un banner sin cookies que gestionar confunde al lector y obliga a mantener un componente más. Si no hay cookies no esenciales, lo correcto es decirlo en la política.
Conclusión
La mayoría de los problemas no eran técnicos sino de coherencia: páginas legales generadas desde una plantilla que describían un sitio que no existe. Las correcciones fueron pequeñas: dos políticas reescritas, dos huecos eliminados y un aviso de afiliado donde el lector lo ve. Si tu proyecto está hecho con IA, empieza por el inventario de la primera parte y revisa tus propias páginas legales con la misma lupa.
// kit para agencias
// implementación
Más sobre Agentes de IA

- Skills para tus agentes
- Claude Code

- Skills para tus agentes
- Claude Code

- Skills para tus agentes
- Claude Code